/*
 * @Author: Wangjun
 * @Date: 2021-05-30 10:55:30
 * @LastEditTime: 2022-10-27 13:44:58
 * @LastEditors: Wangjun
 * @Description:条件函数
 * @FilePath: \query\where.go
 * hnxr
 */

package query

import (
	"errors"
	"reflect"
	"regexp"
	"strings"

	"gitee.com/haodreams/libs/easy"
)

/**
 * @description: ?>value
 * @param {string} key
 * @param {float64} value
 * @return {*}
 */
func (m *Table) GT(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a > b
	}
	floatFunc := func(a, b float64) bool {
		return a > b
	}
	stringFunc := func(a, b string) bool {
		return a > b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ?>=value
 * @param {string} key
 * @param {float64} value
 * @return {*}
 */
func (m *Table) GE(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a >= b
	}
	floatFunc := func(a, b float64) bool {
		return a >= b
	}
	stringFunc := func(a, b string) bool {
		return a >= b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ?<value
 * @param {string} key
 * @param {float64} value
 * @return {*}
 */
func (m *Table) LT(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a < b
	}
	floatFunc := func(a, b float64) bool {
		return a < b
	}
	stringFunc := func(a, b string) bool {
		return a < b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ?<=value
 * @param {string} key
 * @param {float64} value
 * @return {*}
 */
func (m *Table) LE(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a <= b
	}
	floatFunc := func(a, b float64) bool {
		return a <= b
	}
	stringFunc := func(a, b string) bool {
		return a <= b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ? !=value
 * @param {string} key
 * @param {float64} value
 * @return {*}
 */
func (m *Table) NE(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a != b
	}
	floatFunc := func(a, b float64) bool {
		return a != b
	}
	stringFunc := func(a, b string) bool {
		return a != b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

func (m *Table) Like(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return false
	}
	floatFunc := func(a, b float64) bool {
		return false
	}
	stringFunc := func(a, b string) bool {
		return strings.Contains(a, b)
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

// 正则匹配
func (m *Table) Match(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return false
	}
	floatFunc := func(a, b float64) bool {
		return false
	}
	//b = value 正则字符串
	stringFunc := func(a, b string) bool {
		return strings.Contains(a, b)
	}
	if s, ok := value.(string); ok {
		reg, err := regexp.Compile(s)
		if err != nil {
			return m, err
		}
		stringFunc = func(a, b string) bool {
			return reg.MatchString(a)
		}
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ?=value
 * @param {string} key
 * @param {interface{}} value [string,int64,float64]
 * @return {*}
 */
func (m *Table) EQ(key string, value interface{}, isAnd bool) (*Table, error) {
	intFunc := func(a, b int64) bool {
		return a == b
	}
	floatFunc := func(a, b float64) bool {
		return a == b
	}
	stringFunc := func(a, b string) bool {
		return a == b
	}
	return m.filter(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: ?=value
 * @param {string} key
 * @param {interface{}} value [string,int64,float64]
 * @return {*}
 */
func (m *Table) IN(key string, value interface{}, isAnd bool) (*Table, error) {
	switch value.(type) {
	case []string:
	case []float64:
	default:
		err := errors.New("IN 函数参数类型错误")
		return m, err
	}

	intFunc := func(a int64, b []float64) bool {
		for _, k := range b {
			if a == int64(k) {
				return true
			}
		}
		return false
	}
	floatFunc := func(a float64, b []float64) bool {
		for _, k := range b {
			if a == k {
				return true
			}
		}
		return false
	}
	stringFunc := func(a string, b []string) bool {
		for _, k := range b {
			if a == k {
				return true
			}
		}
		return false
	}
	return m.In(isAnd, key, value, intFunc, floatFunc, stringFunc)
}

/**
 * @description: 过虑满足条件的数据
 * @param {*Query} m
 * @return {*}
 */
func (m *Table) In(
	isAnd bool, //是否是and运算
	key string,
	value interface{},
	intFunc func(a int64, b []float64) bool,
	floatFunc func(a float64, b []float64) bool,
	stringFunc func(a string, b []string) bool,
) (arr *Table, err error) {
	if !isAnd {
		return m.orIn(key, value, intFunc, floatFunc, stringFunc)
	}
	arr = new(Table)
	arr.key = m.key
	if key == "" {
		err = errors.New("缺少列名")
		return
	}
	arr.parent = m.arrayObject
	v := reflect.ValueOf(m.arrayObject)
	n, valueType, ids, err := m.checkPreconditions(v, key)
	if err != nil {
		return
	}
	floatValues, _ := value.([]float64)
	stringValues, _ := value.([]string)

	//如果值类型是数字，则参数类型必须是数字
	if valueType != easy.TypeString {
		if len(floatValues) == 0 {
			err = errors.New("参数类型和值类型不一致")
			return
		}
	}

	vals := reflect.MakeSlice(v.Type(), n, n)
	count := 0
	for i := 0; i < n; i++ {
		field := v.Index(i)
		if field.Kind() == reflect.Ptr {
			if field.IsNil() {
				continue
			}
		}
		elem := reflect.Indirect(field)
		if elem.IsValid() && !elem.CanInterface() {
			continue
		}
		//ie := elem.Interface()
		//log.Println(ie)
		if !elem.IsValid() {
			continue
		}
		elem, err = FieldByIndex(elem, ids)
		if err != nil {
			continue
		}
		if !elem.CanInterface() {
			continue
		}
		switch valueType {
		case easy.TypeFloat:
			fval, err := easy.GetFloat64(elem.Interface())
			if err != nil {
				continue
			}
			if floatFunc(fval, floatValues) {
				vals.Index(count).Set(field)
			} else {
				continue
			}
		case easy.TypeInt:
			ival, err := easy.GetInt64(elem.Interface())
			if err != nil {
				continue
			}
			if intFunc(ival, floatValues) {
				vals.Index(count).Set(field)
				//log.Printf("%v", field.Type().String())
			} else {
				continue
			}
		case easy.TypeString:
			if stringFunc(elem.String(), stringValues) {
				vals.Index(count).Set(field)
			} else {
				continue
			}
		default:
			continue
		}
		count++
	}

	if count > 0 {
		x := reflect.New(v.Type())
		x.Elem().Set(vals)
		x.Elem().SetLen(count)
		arr.arrayObject = x.Elem().Interface()
	} else {
		err = errNoOkData
	}
	return arr, err
}

/**
 * @description: 过虑满足条件的数据
 * @param {*Query} m
 * @return {*}
 */
func (m *Table) orIn(
	key string,
	value interface{},
	intFunc func(a int64, b []float64) bool,
	floatFunc func(a float64, b []float64) bool,
	stringFunc func(a string, b []string) bool,
) (arr *Table, err error) {
	arr = new(Table)
	arr.key = m.key
	//or 运算，继承父对象
	arr.parent = m.parent

	v := reflect.ValueOf(m.parent)
	n, valueType, ids, err := m.checkPreconditionsOr(v, key)
	if err != nil {
		if !(err == errNoData || err == errNoOkData) {
			return
		}
	}
	mp := m.add2Map()

	//如果值类型是数字，则参数类型必须是数字
	floatValues, _ := value.([]float64)
	stringValues, _ := value.([]string)

	//如果值类型是数字，则参数类型必须是数字
	if valueType != easy.TypeString {
		if len(floatValues) == 0 {
			err = errors.New("参数类型和值类型不一致")
			return
		}
	}

	for i := 0; i < n; i++ {
		field := v.Index(i)
		if field.Kind() == reflect.Ptr {
			if field.IsNil() {
				continue
			}
		}
		addr := field.Pointer()
		elem := reflect.Indirect(field)
		if elem.IsValid() && !elem.CanInterface() {
			continue
		}
		//ie := elem.Interface()
		//log.Println(ie)
		if !elem.IsValid() {
			continue
		}
		elem, err = FieldByIndex(elem, ids)
		if err != nil {
			continue
		}
		if !elem.CanInterface() {
			continue
		}
		switch valueType {
		case easy.TypeFloat:
			fval, err := easy.GetFloat64(elem.Interface())
			if err != nil {
				continue
			}
			if floatFunc(fval, floatValues) {
				if _, ok := mp[addr]; !ok {
					mp[addr] = field
				}
			} else {
				continue
			}
		case easy.TypeInt:
			ival, err := easy.GetInt64(elem.Interface())
			if err != nil {
				continue
			}
			if intFunc(ival, floatValues) {
				if _, ok := mp[addr]; !ok {
					mp[addr] = field
				}
			} else {
				continue
			}
		case easy.TypeString:
			if stringFunc(elem.String(), stringValues) {
				if _, ok := mp[addr]; !ok {
					mp[addr] = field
				}
			} else {
				continue
			}
		default:
			continue
		}
	}

	if len(mp) == 0 {
		err = errNoOkData
	} else {
		vals := reflect.MakeSlice(v.Type(), len(mp), n)
		count := 0
		for _, val := range mp {
			vals.Index(count).Set(val)
			count++
		}
		arr.arrayObject = vals.Interface()
		err = nil
	}
	return
}
